package com.lab.idea.bl.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Stack;

/**
 * @author mao
 */
public class BlJudge {

    private static final List<Character> JUDGE = CollUtil.newArrayList('<','>','=','≥','≤','±');
    private static final List<Character> OP = CollUtil.newArrayList('+','-','*','/');


    /**
     * 记录已经转换的数值
     */
    private final Stack<BigDecimal> numberStack = new Stack<>();
    /**
     * 记录比较符
     */
    private final Stack<Character> opStack = new Stack<>();
    /**
     * 记录@xxx参数
     */
    private final Stack<Character> argStack = new Stack<>();

    /**
     * 临时读取数值
     */
    private final Stack<Character> valStack = new Stack<>();

    public BlJudge(){

    }

    /**
     * 规则判定
     * @param rule      规则
     * @param value     值 - 数字类型
     * @param env       环境参数
     * @return          是否满足
     */
    public boolean judge(String rule, String value, JSONObject env){
        if (StrUtil.isBlankIfStr(rule) || StrUtil.isBlankIfStr(value)) {
            return false;
        }
        numberStack.add(Convert.toBigDecimal(value,BigDecimal.ZERO));
        char[] chars = StrUtil.trim(rule).toCharArray();
        for (char aChar : chars) {
            if (JUDGE.contains(aChar) || OP.contains(aChar)) {
                toStack(env);
                if (!opStack.isEmpty() && numberStack.size() > 1) {
                    match();
                }
                opStack.push(aChar);
            } else if (aChar == '@' || !argStack.isEmpty()) {
                argStack.push(aChar);
            } else {
                valStack.add(aChar);
            }
        }
        toStack(env);
        if (numberStack.size() > 1) {
            return opJudge();
        }
        return false;
    }

    private void toStack(JSONObject env){
        if (!argStack.isEmpty()) {
            String pop = trackPop(argStack);
            List<String> split = StrUtil.split(pop, "@");
            for (String s : split) {
                String prop = StrUtil.trim(s);
                if (env.containsKey(prop)) {
                    numberStack.push(env.getBigDecimal(prop));
                }
            }
        } else if (!valStack.isEmpty()) {
            numberStack.push(Convert.toBigDecimal(trackPop(valStack)));
        }
    }

    private void match(){
        Character pop = opStack.pop();
        BigDecimal right = numberStack.pop();
        BigDecimal left = numberStack.pop();
        if (OP.get(0).equals(pop)) {
            numberStack.push(left.add(right));
        } else if (OP.get(1).equals(pop)) {
            numberStack.push(left.subtract(right));
        } else if (JUDGE.get(2).equals(pop)) {
            numberStack.push(left.multiply(right));
        } else if (JUDGE.get(3).equals(pop)) {
            numberStack.push(left.divide(right,2, RoundingMode.HALF_UP));
        }
    }

    private boolean opJudge(){
        Character pop = opStack.pop();
        BigDecimal right = numberStack.pop();
        BigDecimal left = numberStack.pop();
        if (JUDGE.get(0).equals(pop)) {
            return left.compareTo(right) < 0;
        } else if (JUDGE.get(1).equals(pop)) {
            return left.compareTo(right) > 0;
        } else if (JUDGE.get(2).equals(pop)) {
            return left.compareTo(right) == 0;
        } else if (JUDGE.get(3).equals(pop)) {
            return left.compareTo(right) >= 0;
        } else if (JUDGE.get(4).equals(pop)) {
            return left.compareTo(right) <= 0;
        }
        return false;
    }


    private String trackPop(Stack<Character> stack){
        StringBuilder builder = new StringBuilder();
        while (!stack.isEmpty()) {
            builder.append(stack.pop());
        }
        return builder.reverse().toString();
    }
}
